/*
* Copyright (C) by Courtanet, All Rights Reserved.
*/
package org.mdl4ui.maven;
import static java.util.Arrays.asList;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.project.MavenProject;
import org.mdl4ui.base.model.BlockID;
import org.mdl4ui.base.model.DependencyPath;
import org.mdl4ui.base.model.FieldDependency;
import org.mdl4ui.base.model.FieldID;
import org.mdl4ui.base.model.GroupID;
import org.mdl4ui.base.model.ScreenID;
import org.mdl4ui.maven.util.BundleFieldFactoryDelegate;
abstract class AbstractDepsMojo extends AbstractMojo {
/**
* @parameter
* @required
*/
private List<String> fieldDependencyClasses;
/**
* @parameter
* @required
*/
private List<String> screenClasses;
/**
* @parameter default-value="${project.build.outputDirectory}"
* @required
*/
private File buildDirectory;
/**
* @parameter default-value="${project}"
* @required
* @readonly
*/
private MavenProject project;
/**
* @parameter default-value="org.mdl4ui.fields.sample.BundleFieldFactory"
* @required
*/
private String bundleFieldFactoryClass;
List<String> getFieldDependencyClasses() {
return fieldDependencyClasses;
}
List<String> getScreenClasses() {
return screenClasses;
}
String getBundleFieldFactoryClass() {
return bundleFieldFactoryClass;
}
final List<ScreenID> filterScreenClass(String packageName, ClassLoader classLoader) throws MojoExecutionException {
for (String screenClassName : screenClasses) {
if (!screenClassName.startsWith(packageName))
continue;
final Class<?> screenClass = forName(screenClassName, classLoader);
return getScreenID(screenClass);
}
throw new IllegalArgumentException(packageName);
}
final ClassLoader getClassLoader() throws MojoFailureException {
final List<?> classpathFiles;
try {
classpathFiles = project.getCompileClasspathElements();
} catch (DependencyResolutionRequiredException e) {
throw new MojoFailureException(e.getMessage());
}
final URL[] urls = new URL[classpathFiles.size() + 1];
try {
for (int i = 0; i < classpathFiles.size(); ++i) {
getLog().debug((String) classpathFiles.get(i));
urls[i] = new File((String) classpathFiles.get(i)).toURI().toURL();
}
urls[classpathFiles.size()] = new File(buildDirectory + "/classes").toURI().toURL();
} catch (MalformedURLException e) {
throw new MojoFailureException(e.getMessage());
}
return new URLClassLoader(urls, Thread.currentThread().getContextClassLoader());
}
final void computeAllDeps(ClassLoader classLoader, Map<FieldID, List<FieldID>> allDeps,
Map<FieldID, List<DependencyPath>> allDeepDeps) throws MojoExecutionException {
for (String fieldDependencyClass : getFieldDependencyClasses()) {
final Class<?> clazz = forName(fieldDependencyClass, classLoader);
final List<FieldDependency> dependencies = getFieldDependencies(clazz);
for (FieldDependency dependency : dependencies) {
if (dependency.from() == null)
continue;
if (dependency.to() == null)
continue;
if (dependency.to().length == 0)
continue;
allDeps.put(dependency.from(), Arrays.asList(dependency.to()));
}
for (FieldDependency dependency : dependencies) {
if (dependency.from() == null)
continue;
if (dependency.to() == null)
continue;
if (dependency.to().length == 0)
continue;
final LinkedList<FieldID> path = new LinkedList<FieldID>();
path.addLast(dependency.from());
final List<DependencyPath> deepDeps = new ArrayList<DependencyPath>();
computeDeepDeps(dependency.from(), path, allDeps, deepDeps, 0);
allDeepDeps.put(dependency.from(), deepDeps);
}
}
}
final void computeDeepDeps(FieldID fieldId, LinkedList<FieldID> path, Map<FieldID, List<FieldID>> allDeps,
List<DependencyPath> deepDeps, int depth) {
if (depth > 10)
return;
final List<FieldID> deps = allDeps.get(fieldId);
if (deps == null)
return;
for (FieldID dep : deps) {
path.addLast(dep);
final DependencyPath existingDep = get(deepDeps, dep);
if (existingDep == null) {
if (path.getFirst() == path.getLast()) {
// detect potential cycle in dependencies graph
continue;
}
deepDeps.add(new DependencyPath(dep, path));
} else {
// detect potential multiple path to the same field
List<FieldID> existingPath = asList(existingDep.getPath());
if (path.size() != existingPath.size()
&& (path.containsAll(existingPath) || existingPath.containsAll(path))) {
getLog().warn(path + " dependency " + dep + " aleady added to " + path.getFirst() + " with path "
+ existingPath);
return;
}
}
path.removeLast();
}
for (FieldID dep : deps) {
path.addLast(dep);
computeDeepDeps(dep, path, allDeps, deepDeps, depth + 1);
path.removeLast();
}
}
static DependencyPath get(List<DependencyPath> deps, FieldID fieldId) {
for (DependencyPath dep : deps) {
if (dep.getFieldId().equals(fieldId))
return dep;
}
return null;
}
static final void createParentFolder(File file) {
if (file.isDirectory())
file.mkdirs();
else
file.getParentFile().mkdirs();
}
static BundleFieldFactoryDelegate getBundleFactoy(String bundleFactoryClass, ClassLoader classLoader)
throws MojoExecutionException {
final Class<?> clazz = forName(bundleFactoryClass, classLoader);
try {
return new BundleFieldFactoryDelegate(clazz, clazz.getField("INSTANCE").get(null));
} catch (IllegalArgumentException e) {
throw new MojoExecutionException(e.getMessage());
} catch (SecurityException e) {
throw new MojoExecutionException(e.getMessage());
} catch (IllegalAccessException e) {
throw new MojoExecutionException(e.getMessage());
} catch (NoSuchFieldException e) {
throw new MojoExecutionException(e.getMessage());
}
}
static List<ScreenID> getScreenID(Class<?> fieldScreenClass) throws MojoExecutionException {
try {
final List<ScreenID> screens = Arrays
.asList((ScreenID[]) fieldScreenClass.getMethod("values").invoke(null));
Collections.sort(screens, new Comparator<ScreenID>() {
@Override
public int compare(ScreenID s1, ScreenID s2) {
return s1.toString().compareTo(s2.toString());
}
});
return screens;
} catch (IllegalArgumentException e) {
throw new MojoExecutionException(e.getMessage());
} catch (SecurityException e) {
throw new MojoExecutionException(e.getMessage());
} catch (IllegalAccessException e) {
throw new MojoExecutionException(e.getMessage());
} catch (InvocationTargetException e) {
throw new MojoExecutionException(e.getMessage());
} catch (NoSuchMethodException e) {
throw new MojoExecutionException(e.getMessage());
}
}
static List<FieldDependency> getFieldDependencies(Class<?> fieldDependencyClass) throws MojoExecutionException {
try {
final List<FieldDependency> deps = Arrays.asList((FieldDependency[]) fieldDependencyClass.getMethod(
"values").invoke(null));
Collections.sort(deps, new Comparator<FieldDependency>() {
@Override
public int compare(FieldDependency b1, FieldDependency b2) {
return b1.toString().compareTo(b2.toString());
}
});
return deps;
} catch (IllegalArgumentException e) {
throw new MojoExecutionException(e.getMessage());
} catch (SecurityException e) {
throw new MojoExecutionException(e.getMessage());
} catch (IllegalAccessException e) {
throw new MojoExecutionException(e.getMessage());
} catch (InvocationTargetException e) {
throw new MojoExecutionException(e.getMessage());
} catch (NoSuchMethodException e) {
throw new MojoExecutionException(e.getMessage());
}
}
static Class<?> forName(String className, ClassLoader classLoader) throws MojoExecutionException {
try {
return Class.forName(className, true, classLoader);
} catch (ClassNotFoundException e) {
throw new MojoExecutionException(e.getMessage());
}
}
static final String formatStaticImports(Set<String> staticImports) {
final StringBuilder builder = new StringBuilder();
for (String fqcn : staticImports) {
builder.append("import static ");
builder.append(fqcn);
builder.append(".*;\n");
}
return builder.toString();
}
String loadTemplate(String templateFile) throws IOException {
return IOUtils.toString(getClass().getResourceAsStream(templateFile));
}
static final List<FieldID> collectFields(List<ScreenID> screenIds) {
final List<FieldID> fieldIds = new ArrayList<FieldID>();
for (ScreenID screenId : screenIds) {
final List<FieldID> screenFieldIds = screenId.fields();
for (FieldID fieldId : screenFieldIds) {
if (fieldIds.contains(fieldId))
continue;
fieldIds.add(fieldId);
}
}
return fieldIds;
}
static final List<GroupID> collectGroups(List<ScreenID> screenIds) {
final List<GroupID> groupIds = new ArrayList<GroupID>();
for (ScreenID screenId : screenIds) {
final List<GroupID> screenGroupIds = screenId.groups();
for (GroupID groupId : screenGroupIds) {
if (groupIds.contains(groupId))
continue;
groupIds.add(groupId);
}
}
return groupIds;
}
static final List<BlockID> collectBlocks(List<ScreenID> screenIds) {
final List<BlockID> blockIds = new ArrayList<BlockID>();
for (ScreenID screenId : screenIds) {
final List<BlockID> screenBlockIds = screenId.blocks();
for (BlockID blockId : screenBlockIds) {
if (blockIds.contains(blockId))
continue;
blockIds.add(blockId);
}
}
return blockIds;
}
static final String getPackage(Class<?> clazz) {
return clazz.getPackage().getName();
}
}